package main

import (
	"fmt"
	"net"
	"time"
	//    "unsafe"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
//	"strconv"
	"strings"
	"errors"
)
var listener net.Listener

func main() {
	//console_cli(nil)
	var err error
	
	start_time = time.Now().UnixNano() / 1e6
	Print_(0, "load config ...")
	loadconfig()
	//debug =1
	Print_(0, "Starting the server ...")
	listener, err = net.Listen("tcp", config["bindaddr"])
	Print_(0, "connect database ...")
	var database *sqlx.DB
	//Try(func(){database, err = sqlx.Open("mysql", "machine:machine@tcp(127.0.0.1:3306)/machine")
	Try(func(){
		address:= config["mysql_user"]+":"+config["mysql_pass"]+"@tcp("+config["mysql_ip"]+")/"+config["mysql_base"]
		Print_(0,address)
		database, err = sqlx.Open("mysql",address)
		Db = database;
		dblist:=[]string{"log_txt","online_temp","os_bind","relation","userlist"}
		SQL:="select table_name name from information_schema.tables where table_schema=\""+config["mysql_base"]+"\""
		dbsql:=make(map[string]string)
		//数据库数据
			dbsql["log_txt"]="	CREATE TABLE `log_txt` (`name` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,	  `log` text NOT NULL,  `type` tinyint(1) NOT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=MyISAM DEFAULT CHARSET=utf8;"
			dbsql["online_temp"]="	CREATE TABLE `online_temp` (	  `name` char(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,	  `tcp` bigint(20) NOT NULL,	  `tempdata` varchar(10) DEFAULT NULL	) ENGINE=MyISAM DEFAULT CHARSET=utf8;"
			dbsql["os_bind"]="	CREATE TABLE `os_bind` (	  `UID` varchar(16) NOT NULL,	  `osname` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL	) ENGINE=MyISAM DEFAULT CHARSET=utf8;"
			dbsql["relation"]="	CREATE TABLE `relation` (	  `uid` varchar(16) NOT NULL,	  `machineID` text NOT NULL,	  `title` varchar(10) DEFAULT NULL,	  `type` char(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL	) ENGINE=MyISAM DEFAULT CHARSET=utf8;"
			dbsql["userlist"]="	CREATE TABLE `userlist` (	  `UID` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,	  `pass` varchar(16) NOT NULL,	  `name` varchar(16) NOT NULL,	  `mail` varchar(16) NOT NULL	) ENGINE=MyISAM DEFAULT CHARSET=utf8;"
		datax,err:=Exec_SQL(SQL)
		if(err!=nil){return/*Print_(0,err.Error());return*/}
		//[{"name":"bG9nX3R4dA=="},{"name":"b25saW5lX3RlbXA="},{"name":"b3NfYmluZA=="},{"name":"cmVsYXRpb24="},{"name":"dXNlcmxpc3Q="}]
		dbtxt:=ToJson(datax)
		for i:=0;i<len(dblist);i++{
			str:=Tobase64([]byte(dblist[i]))
			if(!strings.Contains(dbtxt, "{\"name\":\""+str+"\"}")){
				_,err:=Exec_SQL(dbsql[dblist[i]])
				Print_(0,"create "+dblist[i]+"...")
				if(err!=nil){err=errors.New("Unable to initialize database");break;}
				Print_(0,"create "+dblist[i]+"done")
			}
		}
		reg_login_deleteall()

	},func (e interface{}){err=errors.New("can't connect mysql server")})
	if err != nil {fmt.Println("open mysql failed,",err);return}
	Print_(0, "Load package os")
	Interface_load()
	
	go console_cli(listener)
	if err != nil {
		Print_(1, "Error listening", err.Error())
		return //终止程序
	}
	// 监听并接受来自客户端的连接
	for !exit_t {
		conn, err := listener.Accept()
		if err != nil {
			if exit_t {
				Print_(console_type_default, "close Accept")
			} else {
				Print_(1, "Error accepting", err.Error())
			}

			return // 终止程序
		}
		go client_enter(makesockc(conn,0))
	}

	listener.Close()
}
func console_cli(listener net.Listener) {
	_pt := func(str ...string) { Print_(console_type_default|16, str...) }
	//_ptc := func(str ...string) { Print_(console_type_console|16, str...) }
	_pt("===========================")
	_pt("")
	_pt("        version:1.0")
	_pt("")
	_pt("===========================")
	//go test();
	for {
		/*
		line := make([]string, 5) //[5]string{"","","","",""}
		
		fmt.Scanln(&line)
		
		fmt.Scanln(&line[0], &line[1], &line[2], &line[3],&line[4],&line[5],&line[6])*/
		//Print_(0,"test\ntest")
		//str :=make([]string,5)
		//line := make([]string, 5)
		//time.Sleep(time.Second * 1)

		fmt.Print(">")
		//Print_(0,"1")
		//fmt.Scanln(&line[0], &line[1], &line[2])
		//line:=make([]string,20)
		//fmt.Scanln(&line[0],&line[1],&line[2],&line[3],&line[4],&line[5],&line[6],&line[7],&line[8],&line[9],&line[10],&line[11],&line[12],&line[13],&line[14],&line[15],&line[16],&line[17],&line[18],&line[19])

		line:=Cli_read_line()
		line=resize(line,20)
		if(line[0]==""){
		
		}else if(line[0]=="vmstart"){
				APP_Interface_VM_Console_Start()
		}else if(line[1]=="help"){
		}else if(line[0]=="debug"){
			debug=line[1]=="1"
			if(debug==true){
				_pt("DEBUG mode is turn on")
			}else{
				_pt("DEBUG mode is turn off")
			}
		}else{
			s,e:=Call(line[0],line[0],line[1],line[2],line[3],line[4],line[5],line[6],line[7],line[8],line[9],line[10],line[11],line[12],line[13],line[14],line[15],line[16],line[17],line[18],line[19])
			if(line[0]=="exit"){break}
			if(e!=nil){Print_(console_type_error,e.Error())}else{_pt(fmt.Sprintf("%s",s[0]))}
			
		}


		
	}
}


func client_enter(conn *sockc) {
	//getsockaddr(&conn)
	var UID string
	var cli_type string
	pool_dealwith(conn, nil, 1, &UID) //用户进入
	for {
		
		if err := conn.SetReadDeadline(time.Now().Add(time.Second * 15)); err != nil {
			fmt.Printf("error :%s\n", err)
			break
		}
		buf := make([]byte, 1024)
		lenx, err := conn.Read(buf)
		if err != nil {
			txt := fmt.Sprintf("%s", err)
			if strings.Contains(txt, "i/o timeout") {
				Print_(console_type_debug,"time out")
				_, err := conn.Write([]byte{0})
				if err != nil {break}
				continue;//没有处理事件
			} else if txt == "EOF" {break /*终止程序*/} else {fmt.Printf("error :%s\n", err);break}
		}
		if(conn.init==false){conn.init=true;if(WsHand(conn,buf[:lenx])){continue}}//如果是websocket则多一步验证而已
		
		pool_dealwith(conn, buf[:lenx], 2, &UID,&cli_type) //收到数据
		if err := conn.SetReadDeadline(time.Time{}); err != nil {break}
	}
	pool_dealwith(conn, nil, 3, &UID) //用户退出
	defer conn.Close()
}
func pool_dealwith(conn *sockc, data []byte, type_ int, avg ...*string) {
	
	//Print_(console_type_debug, string(data))
	//_data:=toclient(&data)
	_data := new(client_data)
	if len(data) == 0 {
		_data.lx = 0
		_data.data = []byte{}
	} else {
		_data.lx = uint8(data[0])
		_data.data = data[1:]
	}
	if len(avg) == 0 {
	} else {

		if string(*avg[0]) == "" { //没有验证的
			//fmt.Println(_data.lx,_data.data)
			if type_ == 2 {
				//数据处理
				if _data.lx == 1 {
					//验证信息
					//x:=[]byte()
					datax := ([]byte)(_data.data[:])
					logindata := toldt(&datax)
					username := string(logindata.UID[:])
					password := string(logindata.password[:])
					if len(username) != 16 {
						return
					}
					if checknum(username) {
						//用户登录
						Print_(console_type_debug, "login", username, password)
						if user_login_check(username, password) {
							*avg[0] = string(logindata.UID[:])
							if(reg_login_token(conn, *avg[0])){
								//登录成功！
								*avg[1]="1"//声明是用户设备
								SendMessage_Text(conn,"logined")
							}else{
								//注册登录失败
								SendMessage_Text(conn,"login warm")
								Print_(0,"login warm")
								defer conn.Close()//密码错误则断开
							}

						} else {
							//登录失败
							SendMessage_Text(conn,"login error")
							defer conn.Close()//密码错误则断开
						}
					} else {
						//物联网设备登录
						*avg[0] = string(logindata.UID[:])
						
						//isCreatVM:=
						if reg_login_token(conn, *avg[0]) {
							
							_,err:=Exec_SQL(fmt.Sprintf("insert into log_txt(name,log,type)values(\"%s\",\"%s\",\"%d\")", *avg[0], "logined at "+conn.RemoteAddr().String(), 1))
							if(err!=nil){Print_(console_type_error,err.Error())}
							
							*avg[1]="0"//声明是物联网设备
							if(logindata.password[0]==1){//需要创建VM虚拟机
								Print_(0,"prepare make vm")
								name:=""
								err:=Db.Get(&name, "select osname from os_bind where UID=?",*avg[0])
								msg:=[]byte(*avg[0]+"Not have set os name")
								msg=byte_add_to_bytes([]byte{13},msg)
								if(err==nil){Print_(console_type_debug,"CreatVM "+*avg[0]+"@("+name+")");CreatVM(*avg[0],getsockaddr(conn),name);
								_,err=Interface_console(*avg[0])("Init()")
								if(err!=nil){Print_(console_type_debug,err.Error())}
								}else{
									Print_(console_type_error,string(msg[1:]))
									Broadcast_message(conn,*avg[0],msg)/*广播错误消息*/}
								
							}
						}

					}

				}
			} else {

			}
		} else if type_ == 2 { //完成认证的
			//Print_(0,"TYPE:"+ToString(_data.lx))
			if _data.lx == 2 {
				//客户端确认接收

			} else if _data.lx == 3 {
				//客户端控制台输出的信息
				Broadcast_message(conn, *avg[0], data)
			} else if _data.lx == 4 {
				//客户端控制台产生的错误消息
				Broadcast_message(conn, *avg[0], data)
			} else if _data.lx == 5 {
				//转发信息到客户端
				UID := (string)(_data.data[0:16])
				conn, err := getsock(UID_get_sock(UID))
				if err != nil {
					//客户端不在线
				} else {
					//客户端在线
					conn.Write(_data.data[16:])
				}
			} else if(_data.lx==6){
				Print_(console_type_debug,"Ctrl vm " + *avg[0]+" "+*avg[1])
				if(*avg[1]=="0"){//如果是物联网设备就允许进入虚拟机
					Print_(console_type_debug,"enter")
					Interface_OnMessage(*avg[0],0,_data.data,0)
				}else if(*avg[1]=="1"){//如果事客户端就进行控制
					if(len(_data.data)<17){/*数据不完整*/Print_(console_type_debug,"[error] data len < 17");return}
					UID:=(string)(_data.data[0:16])
					Print_(console_type_debug,"UID"+UID+":"+string(_data.data[16:]))
					Interface_OnMessage(UID,1,_data.data[16:],getsockaddr(conn))
					//ce:=Interface_console(UID)
					//if(ce==nil){Print_(console_type_debug,"Vm not start");SendMessage_Text(conn,"Vm not start");return}
					//result,err:=ce(_data.data[16:])
					//s,_:=result.ToString()
					//if(err!=nil){SendMessage_Text(conn,err.Error())}else{SendMessage_Text(conn,s)}
				}
				
			}else if(_data.lx==7){
				if(len(_data.data)<17){/*数据不完整*/Print_(console_type_debug,"[error] data len["+ToString(len(_data.data))+"] < 17");return}
				
				UID:=(string)(_data.data[0:16])
				Print_(console_type_debug,"UID"+UID+":"+string(_data.data[16:]))
				ce:=Interface_console(UID)
				if(ce==nil){Print_(console_type_debug,"Vm not start");SendMessage_Text(conn,"Vm not start");return}
				result,err:=ce(_data.data[16:])
				s,_:=result.ToString()
				if(err!=nil){SendMessage_Text(conn,err.Error())}else{SendMessage_Text(conn,s)}
			}else if(_data.lx==8){//获取关联列表及相关状态
				
				list,err:=Exec_SQL("Select machineID id,title,tcp port,type,tempdata data  from relation LEFT JOIN online_temp ON machineID=name where relation.uid=?",*avg[0])
				if(err!=nil){Print_(console_type_debug,err.Error())}
				Print_(console_type_debug,ToJson(list))
				_,err=conn.Write([]byte(string(8)+ToJson(list)))
				if(err!=nil){fmt.Printf("err:%s",err.Error())}
				
				//Print_(0,)
				//if(err!=)
				//conn.Write([]byte("\001"+ToJson(list)))
				//Interface_OnMessage

			
			}else if _data.lx == 255 {
				//ping消息
				if len(_data.data) < 24 {
					return
				}
				timex := (int)(get_times() - BytesToInt64(_data.data[0:8]))
				PID := (string)(_data.data[8:13])
				go StopPing(PID, timex)

			}
		} else if type_ == 3 { //用户退出
			if(*avg[0]!=""){//已登录的才允许被记录
				
				Print_(console_type_debug,"user logout"+*avg[0])
				_,err:=Exec_SQL(fmt.Sprintf("insert into log_txt(name,log,type)values(\"%s\",\"%s\",\"%d\")", *avg[0], "login out", 2))
				if(err!=nil){Print_(console_type_error,err.Error())}
			}
			
		}
	}
}

